#include "PaVar.h"
#include <QFile>
#include <QJsonParseError>
#include <QFileInfo>
#include <QDir>
#include "../XXqtDefine.h"
#include "./XXlogger.h"

QVariant PaVar::get(const QVariantList &keys) const{
    QVariant temp = _root;
    foreach(auto key, keys){
        if(key.type()==QVariant::Int && temp.type()==QVariant::List){
            temp = temp.toList().value(key.toInt(),QVariant());
        }
        else if (key.type()==QVariant::String && temp.type()==QVariant::Map) {
            temp = temp.toMap().value(key.toString(),QVariant());
        }
        else {
            temp = QVariant();
            break;
        }
    }
    return temp;
}
void PaVar::set(const QVariantList &keys, const QVariant &value){
    if(keys.isEmpty()){
        _root = value;
        return;
    }

    VariantPairList temp;
    addNode(_root,temp,keys.first());
    for (auto iter=keys.begin()+1; iter!=keys.end(); iter++) {
        addNode(temp,*iter);
    }

    auto last = temp.takeLast();
    last.second = value;
    QVariant key = last.first;
    QVariant val = last.second;

    for (auto iter=temp.rbegin(); iter!=temp.rend(); iter++) {
        auto parent = iter->second;
        if(!saveNode(parent,key,val)){
            xxLogStr(QString("invalid keys. keys:")+keyString(keys));
            return;
        }
        iter->second = parent;

        key = iter->first;
        val = iter->second;
    }
    if(!saveNode(_root,key,val)){
        xxLogStr(QString("invalid keys. keys:")+keyString(keys));
    }
}
void PaVar::del(const QVariantList &keys){
    if(keys.isEmpty()){
        if(_root.type() == QVariant::List){
            _root = QVariantList();
        }
        else if(_root.type() == QVariant::Map){
            _root = QVariantMap();
        }
        else{
            return;
        }
    }
    set(keys,QVariant());
}
bool PaVar::exists(const QVariantList &keys) const{
    return !get(keys).isNull();
}
int PaVar::count(const QVariantList &keys) const{
    auto var = get(keys);
    Q_ASSERT(var.type() == QVariant::Map || var.type() == QVariant::List);

    if(var.type() == QVariant::Map){
        return var.toMap().count();
    }
    else if(var.type() == QVariant::List){
        return var.toList().count();
    }
    else{
        return -1;
    }
}

void PaVar::append(const QVariantList &keys, const QVariant &value){
    auto var = get(keys);
    if(var.isNull()){
        set(QVariantList(keys)<<0,value);
    }
    else if(var.type() == QVariant::List){
        auto list = var.toList();
        list.append(value);
        set(keys,list);
    }
    else{

    }
}
void PaVar::insert(const QVariantList &keys, int index, const QVariant &value){
    auto var = get(keys);
    if(var.isNull() && 0==index){
        set(QVariantList(keys)<<0,value);
    }
    else if(var.type() == QVariant::List){
        auto list = var.toList();
        list.insert(index,value);
        set(keys,list);
    }
    else{

    }
}
QVariant PaVar::at(const QVariantList &keys, int index) const{
    auto var = get(keys);
    Q_ASSERT(!var.isNull());
    auto list = var.toList();
    Q_ASSERT(list.count()>index);
    return list.at(index);
}
QVariant PaVar::first(const QVariantList &keys) const{
    auto var = get(keys);
    Q_ASSERT(!var.isNull());
    auto list = var.toList();
    Q_ASSERT(!list.isEmpty());
    return list.first();
}
QVariant PaVar::last(const QVariantList &keys) const{
    auto var = get(keys);
    Q_ASSERT(!var.isNull());
    auto list = var.toList();
    Q_ASSERT(!list.isEmpty());
    return list.last();
}
int PaVar::indexAt(const QVariantList &keys, const QVariant &value) const{
    auto var = get(keys);
    Q_ASSERT(!var.isNull());
    auto list = var.toList();
    return list.indexOf(value,-1);
}
void PaVar::removeAt(const QVariantList &keys, int index){
    auto var = get(keys);
    Q_ASSERT(!var.isNull());
    auto list = var.toList();
    Q_ASSERT(list.count()>index);
    list.removeAt(index);
}
QVariant PaVar::takeFirst(const QVariantList &keys){
    auto var = get(keys);
    Q_ASSERT(!var.isNull());
    auto list = var.toList();
    return list.takeLast();
}
QVariant PaVar::takeLast(const QVariantList &keys){
    auto var = get(keys);
    Q_ASSERT(!var.isNull());
    auto list = var.toList();
    return list.takeLast();
}

void PaVar::addNode(QList<VariantPair> &varList, const QVariant &key){
    VariantPair &pair = varList.last();
    if(key.type()==QVariant::String && pair.second.type()==QVariant::Map){
        auto map = pair.second.toMap();
        varList << VariantPair(key,map.value(key.toString(),QVariant()));
    }
    else if(key.type()==QVariant::Int && pair.second.type()==QVariant::List){
        auto list = pair.second.toList();
        varList << VariantPair(key,list.value(key.toInt(),QVariant()));
    }
    else{
        varList << VariantPair(key,QVariant());
    }
}
void PaVar::addNode(const QVariant &parentVar, QList<VariantPair> &varList, const QVariant &key){
    if(key.type()==QVariant::Int && parentVar.type()==QVariant::List){
        auto list = parentVar.toList();
        varList << VariantPair(key,list.value(key.toInt(),QVariant()));
    }
    else if(key.type()==QVariant::String && parentVar.type()==QVariant::Map){
        auto map = parentVar.toMap();
        varList << VariantPair(key,map.value(key.toString(),QVariant()));
    }
    else{
        varList << VariantPair(key,QVariant());
    }
}
bool PaVar::saveNode(QVariant &parent, const QVariant &key, const QVariant &value){
    if(key.type()==QVariant::Int){
        return saveListNode(parent,key.toInt(),value);
    }
    else if(key.type()==QVariant::String){
        return saveMapNode(parent,key.toString(),value);
    }
    else{
        return false;
    }
}
bool PaVar::saveListNode(QVariant &parent, int index, const QVariant &value){
    if(parent.type()==QVariant::List){
        QVariantList list = parent.toList();
        if(index > list.count() || index < 0){
            xxLog("[Error] failure to save value.");
            return false;
        }
        else if(index == list.count()){
            if(!value.isNull()){
                list << value;
                parent = list;
            }
        }
        else{
            if(value.isNull()){
                list.removeAt(index);
            }
            else{
                list[index] = value;
            }
            parent = list;
        }
        return true;
    }
    else if(parent.type()==QVariant::Invalid){
        if(0 != index){
            xxLog("[Error] failure to save value.");
            return false;
        }
        QVariantList list = {value};
        parent = list;
        return true;
    }
    else{
        return false;
    }
}
bool PaVar::saveMapNode(QVariant &parent, const QString &string, const QVariant &value){
    if(parent.type()!=QVariant::Map && parent.type()!=QVariant::Invalid){
        xxLog("[Error] failure to save value.");
        return false;
    }

    QVariantMap map = parent.type()==QVariant::Map ? parent.toMap() : QVariantMap();
    if(value.isNull()){
        map.remove(string);
    }
    else{
        map[string] = value;
    }
    parent = map;
    return true;
}

QString PaVar::keyString(const QVariantList &keys){
    QString msg;
    foreach(auto key,keys){
        msg += "["+key.value<QString>()+"]";
    }
    return msg;
}


PaVarRef::PaVarRef(PaVar &paVar)
    :PaKey(),_paVar(paVar){
}
PaVarRef::PaVarRef(const PaVarRef &paVarRef)
    :PaKey(),_paVar(paVarRef._paVar){
    _keys = paVarRef._keys;
}
PaVarRef PaVarRef::operator[](const QVariant &key) const{
    PaVarRef ref = *this;
    ref<<key;
    return ref;
}
void PaVarRef::operator=(const PaVarRef &paVarRef){
    setValue(paVarRef.value());
}
void PaVarRef::operator=(const QVariant &value){
    setValue(value);
}

QVariant PaVarRef::toValue() const{
    return value();
}
QVariantList PaVarRef::toList()const{
    return value().toList();
}
QVariantMap PaVarRef::toMap() const{
    return value().toMap();
}
int PaVarRef::toInt() const{
    return value().toInt();
}
QString PaVarRef::toString() const{
    return value().toString();
}
QString PaVarRef::toJsonString(bool indented) const{
    return XXqtDefine::variantToJsonString(value(),indented);
}
void PaVarRef::fromJsonString(const QString &jsonString){
    setValue(XXqtDefine::variantFromJsonString(jsonString));
}

void PaVarRef::del(){
    _paVar.del(_keys);
}
bool PaVarRef::exists() const{
    return _paVar.exists(_keys);
}
int PaVarRef::count() const{
    return _paVar.count(_keys);
}

// list
void PaVarRef::append(const QVariant &value){
    _paVar.append(_keys,value);
}
void PaVarRef::insert(int index, const QVariant &value){
    _paVar.insert(_keys,index,value);
}
QVariant PaVarRef::at(int index) const{
    return _paVar.at(_keys,index);
}
QVariant PaVarRef::first() const{
    return _paVar.first(_keys);
}
QVariant PaVarRef::last() const{
    return _paVar.last(_keys);
}
int PaVarRef::indexAt(const QVariant &value) const{
    return _paVar.indexAt(_keys,value);
}
void PaVarRef::removeAt(int index){
    _paVar.removeAt(_keys,index);
}
QVariant PaVarRef::takeFirst(){
    return _paVar.takeFirst(_keys);
}
QVariant PaVarRef::takeLast(){
    return _paVar.takeFirst(_keys);
}

QVariant PaVarRef::value() const{
    return _paVar.get(_keys);
}
void PaVarRef::setValue(const QVariant &value){
    _paVar.set(_keys,value);
}

